package com.iceframework.core.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class BigDecimalUtils {

    /**
     * Adds two objects after converting them to BigDecimal, treating null as 0.
     * Supports String, Number (Integer, Double, Float, Long, etc.), and BigDecimal types.
     *
     * @param obj1 First object to add
     * @param obj2 Second object to add
     * @return Sum as BigDecimal
     */
    public static BigDecimal add(Object obj1, Object obj2) {
        BigDecimal bd1 = toBigDecimal(obj1, true);
        BigDecimal bd2 = toBigDecimal(obj2, true);
        return bd1.add(bd2);
    }

    /**
     * Subtracts obj2 from obj1 after converting to BigDecimal, treating null as 0.
     *
     * @param obj1 First object
     * @param obj2 Second object to subtract
     * @return Difference as BigDecimal
     */
    public static BigDecimal subtract(Object obj1, Object obj2) {
        BigDecimal bd1 = toBigDecimal(obj1, true);
        BigDecimal bd2 = toBigDecimal(obj2, true);
        return bd1.subtract(bd2);
    }

    /**
     * Multiplies two objects after converting to BigDecimal, treating null as 0.
     *
     * @param obj1 First object
     * @param obj2 Second object
     * @return Product as BigDecimal
     */
    public static BigDecimal multiply(Object obj1, Object obj2) {
        BigDecimal bd1 = toBigDecimal(obj1, true);
        BigDecimal bd2 = toBigDecimal(obj2, true);
        return bd1.multiply(bd2);
    }

    /**
     * Divides obj1 by obj2 after converting to BigDecimal, treating null as 0.
     *
     * @param obj1 First object (dividend)
     * @param obj2 Second object (divisor)
     * @param scale Number of decimal places for rounding
     * @param roundingMode Rounding mode to apply
     * @return Quotient as BigDecimal, or null if division by zero
     */
    public static BigDecimal divide(Object obj1, Object obj2, int scale, RoundingMode roundingMode) {
        BigDecimal bd1 = toBigDecimal(obj1, true);
        BigDecimal bd2 = toBigDecimal(obj2, true);
        
        if (bd2.equals(BigDecimal.ZERO)) {
            return null; // Cannot divide by zero
        }
        
        return bd1.divide(bd2, scale, roundingMode);
    }

    /**
     * Rounds a BigDecimal to the specified scale.
     *
     * @param value BigDecimal to round
     * @param scale Number of decimal places
     * @param roundingMode Rounding mode to apply
     * @return Rounded BigDecimal, or null if input is null
     */
    public static BigDecimal round(BigDecimal value, int scale, RoundingMode roundingMode) {
        if (value == null) {
            return null;
        }
        return value.setScale(scale, roundingMode);
    }

    /**
     * Converts an object to BigDecimal, optionally treating null as 0.
     * Supports String, Number (Integer, Double, Float, Long, etc.), and BigDecimal types.
     *
     * @param obj Object to convert
     * @param nullAsZero If true, null returns BigDecimal.ZERO; if false, null returns null
     * @return BigDecimal representation
     */
    public static BigDecimal toBigDecimal(Object obj, boolean nullAsZero) {
        if (obj == null) {
            return nullAsZero ? BigDecimal.ZERO : null;
        }
        
        try {
            if (obj instanceof BigDecimal) {
                return (BigDecimal) obj;
            } else if (obj instanceof Number) {
                return new BigDecimal(obj.toString());
            } else if (obj instanceof String) {
                String str = ((String) obj).trim();
                if (str.isEmpty()) {
                    return nullAsZero ? BigDecimal.ZERO : null;
                }
                return new BigDecimal(str);
            }
        } catch (NumberFormatException e) {
            return nullAsZero ? BigDecimal.ZERO : null;
        }
        
        return nullAsZero ? BigDecimal.ZERO : null;
    }

    /**
     * Converts an object to BigDecimal, treating null as null.
     *
     * @param obj Object to convert
     * @return BigDecimal representation, or null if conversion fails
     */
    public static BigDecimal toBigDecimal(Object obj) {
        return toBigDecimal(obj, false);
    }

    /**
     * Checks if a BigDecimal is zero or null.
     *
     * @param value BigDecimal to check
     * @return true if value is null or zero, false otherwise
     */
    public static boolean isZeroOrNull(BigDecimal value) {
        return value == null || value.compareTo(BigDecimal.ZERO) == 0;
    }

    /**
     * Compares two objects as BigDecimal values, treating null as 0.
     *
     * @param obj1 First object
     * @param obj2 Second object
     * @return -1 if obj1 < obj2, 0 if equal, 1 if obj1 > obj2
     */
    public static int compare(Object obj1, Object obj2) {
        BigDecimal bd1 = toBigDecimal(obj1, true);
        BigDecimal bd2 = toBigDecimal(obj2, true);
        return bd1.compareTo(bd2);
    }

    /**
     * Returns the maximum of two BigDecimal values, treating null as 0.
     *
     * @param obj1 First object
     * @param obj2 Second object
     * @return Maximum BigDecimal
     */
    public static BigDecimal max(Object obj1, Object obj2) {
        BigDecimal bd1 = toBigDecimal(obj1, true);
        BigDecimal bd2 = toBigDecimal(obj2, true);
        return bd1.compareTo(bd2) >= 0 ? bd1 : bd2;
    }

    /**
     * Returns the minimum of two BigDecimal values, treating null as 0.
     *
     * @param obj1 First object
     * @param obj2 Second object
     * @return Minimum BigDecimal
     */
    public static BigDecimal min(Object obj1, Object obj2) {
        BigDecimal bd1 = toBigDecimal(obj1, true);
        BigDecimal bd2 = toBigDecimal(obj2, true);
        return bd1.compareTo(bd2) <= 0 ? bd1 : bd2;
    }
}